package com.clp.protocol.iec104.server.pipeline.state.data;

import com.clp.protocol.iec104.apdu.asdu.infoobj.CInfoObj;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.M_IT_NA_1_InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.qua.M_IT_NA_1_Qua;
import com.clp.protocol.iec104.definition.ConstVal;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.iec104.server.SlaveChannel;
import lombok.Getter;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

@Getter
public abstract class TotalCall101DataReactor extends DataReactor {
    private final TpDataType tpDataType;

    public TotalCall101DataReactor(Executor executor, TpDataType tpDataType) {
        super(executor);
        this.tpDataType = tpDataType;
    }

    public CompletableFuture<Boolean> accept(SlaveChannel slaveChannel) {
        return supplyAsync(() -> accept0(slaveChannel));
    }

    protected abstract boolean accept0(SlaveChannel slaveChannel);

    public CompletableFuture<List<TpData>> collectTpData(SlaveChannel slaveChannel) {
        return supplyAsync(() -> collectTpData0(slaveChannel));
    }

    protected abstract List<TpData> collectTpData0(SlaveChannel slaveChannel);

    @Getter
    public enum TpDataType {
        CUMULANT(TypeTag.M_IT_NA_1){
            @Override
            public CInfoObj convert(TpData data) {
                M_IT_NA_1_InfoElem infoElem = new M_IT_NA_1_InfoElem(data.getValue());
                M_IT_NA_1_Qua qua = new M_IT_NA_1_Qua(data.isValid(), data.isAdjusted(), data.isOverflowed(), data.getSeq());

                return new CInfoObj(getTypeTag(), data.getAddress(), infoElem, qua, null);
            }

            @Override
            public CInfoObj convertInvalid(int addr) {
                M_IT_NA_1_InfoElem infoElem = new M_IT_NA_1_InfoElem(0);
                M_IT_NA_1_Qua qua = new M_IT_NA_1_Qua(false, false, false, 0);

                return new CInfoObj(getTypeTag(), addr, infoElem, qua, null);
            }
        },
        CUMULANT_WITH_CP56TIME2A(TypeTag.M_IT_TA_1) {
            @Override
            public CInfoObj convert(TpData data) {
                throw new UnsupportedOperationException();
            }

            @Override
            public CInfoObj convertInvalid(int addr) {
                throw new UnsupportedOperationException();
            }
        };

        private final TypeTag typeTag;
        private final int maxCount;

        TpDataType(TypeTag typeTag) {
            this.typeTag = typeTag;
            this.maxCount = (ConstVal.APDU_VARIABLE_SIZE - ConstVal.INFO_ELEM_ADDRESS_LEN) /
                    (typeTag.getInfoElemBytesLen() + ConstVal.VSQ_LEN);
        }

        public abstract CInfoObj convert(TpData data);

        public abstract CInfoObj convertInvalid(int addr);
    }
}
